# coding=utf-8
# encoding:utf-8
import logging
from concurrent.futures import ThreadPoolExecutor
from queue import Queue
import threading
from time import ctime
import time
import requests
import base64
import re
from datetime import datetime
import matplotlib.pyplot as plt
import win32gui, win32ui, win32con, win32print
from PIL import Image
import json

LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
logging.basicConfig(filename='my.log', level=logging.INFO, format=LOG_FORMAT)

request_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/numbers"
access_token = '24.b55f626512d3b01e0ce53a5970972de8.2592000.1648103111.282335-25645467'
request_url = request_url + "?access_token=" + access_token
headers = {'content-type': 'application/x-www-form-urlencoded'}
listx = []
listy = []
print("延时识别将在3秒后开始运行…………\n")
time.sleep(3)

def orc(queue):
    while True:
        task = queue.get() #阻塞在队列中，直到队列非空，取出任务,task为第几秒
        f = open(str(task) + '.jpg', 'rb')
        img = base64.b64encode(f.read())
        params = {"image":img}
        response = requests.post(request_url, data=params, headers=headers)
        if response:
            #测试原来完整的json数据
            #print(response.json())

            responseStr = str(response.json())
            logging.info("第" + str(task) + "次截图的识别信息为：" + responseStr)
            numList = re.findall("(?<=words': ').*?(?=', 'location')", responseStr)
            numStr = str(numList[0])
            #防止orc识别不对
            if (len(numStr) != 16):
                logging.info("第" + str(task) + "次截图的orc识别错误，丢掉该信息")
                continue

            time1 = int(numStr[0: 2])*60*60*1000 + int(numStr[2: 4])*60*1000 + int(numStr[4: 6])*1000 + int(numStr[6: 8])*10
            time2 = int(numStr[8: 10])*60*60*1000 + int(numStr[10: 12])*60*1000 + int(numStr[12: 14])*1000 + int(numStr[14: 16])*10
            delay = time1 - time2
            #识别错误，打印日志并舍弃该数据。
            if (delay < 0):
                logging.info("第" + str(task) + "次截图的orc识别错误，丢掉该信息")
                continue
            print("第"+ str(task) + "秒的延迟为：" + str(delay) + "毫秒")
            logging.info("第"+ str(task) + "次截图的orc识别延迟时间为：" + str(delay) + "毫秒")
            #print("第"+ str(task) + "秒的延迟时间为：" + str(delay) + "毫秒\n")
            #画图 横坐标是task，纵坐标是delay
            listx.append(task)
            listy.append(delay)
            #（已解决）这里暂时有个问题，需要类似于c++的vector<int> a(2), a[0]是横坐标，a[1]是纵坐标，最后需要对于横坐标sort，再画图

def transfer(infile, outfile, w, h):
    im = Image.open(infile)
    reim=im.resize((int(0.6 * w), int(0.6 * h)))#宽*高
    reim.save(outfile,dpi=(1920.0,1080.0)) ##分别为想要设定的dpi值

def window_capture(filename):
    # 根据窗口句柄获取窗口的设备上下文DC（Divice Context）
    hwndDC = win32gui.GetWindowDC(0)
    # 根据窗口的DC获取mfcDC
    mfcDC = win32ui.CreateDCFromHandle(hwndDC)
    # mfcDC创建可兼容的DC
    saveDC = mfcDC.CreateCompatibleDC()
    # 创建bigmap准备保存图片
    saveBitMap = win32ui.CreateBitmap()
    #获取真实的分辨率，也就是乘以缩放之后的
    w = win32print.GetDeviceCaps(hwndDC, win32con.DESKTOPHORZRES)
    h = win32print.GetDeviceCaps(hwndDC, win32con.DESKTOPVERTRES)
    #图片截取处理
    cut_size = int(h * 0.1)
    # 为bitmap开辟空间
    w_cut = w - 2 * cut_size
    h_cut = h - 2 * cut_size
    saveBitMap.CreateCompatibleBitmap(mfcDC, w_cut, h_cut)
    # 高度saveDC，将截图保存到saveBitmap中
    saveDC.SelectObject(saveBitMap)
    # 截图，大概是八个方向都减少屏幕高度的十分之一长度
    saveDC.BitBlt((-cut_size, -cut_size), (w - cut_size, h - cut_size), mfcDC, (0, 0), win32con.SRCCOPY)
    saveBitMap.SaveBitmapFile(saveDC, filename)
    return w_cut, h_cut

def draw_save():
    tmp_dict = {}
    for i in range(len(listx)):
        tmp_dict[listx[i]] = listy[i]
    datas = sorted(tmp_dict.items(), key = lambda e:e[0])

    #异常数据过滤：
    #C++语法
    #for (int i = 0; i < datas.size() - 1; i++) {
    #    if (datas[i] > datas[i + 1] * 3)    
    #        datas.erase(datas[i]);
    #}
    #当前这一秒的延时，如果比下一秒的延时的三倍还大，并且当前这一秒的延时大于1000或者下一秒的延时大于200，则丢弃该数据(最后一秒是漏网之鱼)
    for i in range(len(datas) - 2):
        if ((datas[i][1] > datas[i + 1][1] * 3) and ((datas[i + 1][1] > 200) or (datas[i][1] > 1000))):
            print("第" + str(datas[i][0]) + "次截图的数据有异常，延时为:" + str(datas[i][1]) +"毫秒，请手动查看日志……")
            logging.info("第" + str(datas[i][0]) + "次截图的数据有异常，延时为:" + str(datas[i][1]) + "毫秒")
            del datas[i]

    x_label = []
    y_label = []
    for i in range(len(datas)):
        a = datas[i][0]
        b = datas[i][1]
        x_label.append(datas[i][0])
        y_label.append(datas[i][1])
        logging.info("-> 第" + str(a) + "次截图的延时:" + str(b) + "毫秒  ")


    #print(x_label)
    #print(y_label)
    plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
    plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
    plt.xlabel('时间/秒')
    plt.ylabel('延时/毫秒')
    plt.title("端到端延时识别")
    plt.scatter(x_label, y_label, marker='o', s=10)
    plt.plot(x_label, y_label)
    for a, b in zip(x_label, y_label):  
        plt.text(a, b, (b), va='bottom', fontsize=10)
    plt.savefig("delay_pic.png")
    plt.show()
    print("结果已保存，请查看日志信息……")


def main():
    task_queue = Queue(maxsize=100)
    
    #启动4个线程去执行orc
    #这些子线程会阻塞在消息队列中，有消息了就会执行
    executor = ThreadPoolExecutor(max_workers=4)
    #通过submit函数提交执行的函数到线程池中
    task1 = executor.submit(orc, (task_queue))
    task2 = executor.submit(orc, (task_queue))
    task3 = executor.submit(orc, (task_queue))
    task4 = executor.submit(orc, (task_queue))


    #主线程根据定时器进行截图压缩，并添加到消息队列里
    for x in range(30):
        beg = time.time()
        print("第" + str(x + 1) + "秒")
        #截图
        beg1 = time.time()
        logging.info("第" + str(x + 1) + "次截图")
        w, h = window_capture(str(x + 1)+".jpg") 
        #压缩
        infil= str(x + 1) + ".jpg"
        outfile= str(x + 1) + ".jpg"
        transfer(infil, outfile, w, h)

        end1 = time.time()

        #防止截图和压缩耗时太长，即舍掉该数据。一般是在100ms以内，偶尔会在200ms内，再偶尔一点，试过超过1s
        if (end1 - beg1 >= 1):
            continue
      
        logging.info("第" + str(x + 1) + "次截图和压缩一共所用的时间："+ str(end1 - beg1) + "秒")
        #添加任务到消息队列
        task_queue.put(x + 1)

        #（未解决）定时器间隔1秒，这里的定时器设计有点简陋，会有毫秒级的误差
        end = time.time()
        sleep_time = 1 - (end - beg)
        if sleep_time > 0 :
            time.sleep(sleep_time)

    print("时间到，3秒后画图并保存延时数据到本地……")
    #这里sleep是为了保证线程池中的任务都完成了
    time.sleep(3)
    draw_save()

if __name__ == "__main__":
    main()